#include "Kasiski.h"
#include "string.h"

Kasiski::Kasiski(string pathEntrada, string pathDiccionario) {
	/* Buscar los grupos de caracteres iguales */
	this->pathEntrada = pathEntrada;
	this->pathDiccionario = pathDiccionario;
	this->ocurrenciasEncontradas = new vector<ocurrencia> ();
}

/* Ordenamiento doble de structs de MAYOR a MENOR por frecuencia primero y luego por longitud de la palabra */
struct ordenamientoPorFrecuenciaYLongitud {
	bool operator()(ocurrencia const & a, ocurrencia const & b) {
		if (a.frecuencia == b.frecuencia)
			return a.descripcion.size() < b.descripcion.size();
		else
			return a.frecuencia > b.frecuencia;
	}
};

/* Ordenamiento por frecuencia de menor a mayor */
bool sortDiccionario(ocurrencia i, ocurrencia j) {
	return (i.descripcion.size() < j.descripcion.size());
}

/* Ordenamiento por frecuencia de maoyor a menor */
bool sortDiccionarioPorFrecuencia(ocurrencia i, ocurrencia j) {
	return (i.frecuencia > j.frecuencia);
}

/* Obtengo todas las palabras que se encuentran en el diccionario cargado */
vector<ocurrencia> Kasiski::getPalabrasDelDiccionario() {

	vector<ocurrencia> palabras;

	/* Abro diccionario para usar sus palabras */

	fstream archivoDiccionario;
	string pathDiccionario = this->pathDiccionario;
	archivoDiccionario.open(pathDiccionario.c_str(), ios::in);

	string palabra;
	while (getline(archivoDiccionario, palabra)) {
		ocurrencia palabraDiccionario;

		for (unsigned int p = 0; p < palabra.length(); p++) {
			if (palabra.at(p) == '|')
				break;
			palabraDiccionario.descripcion.push_back(palabra.at(p));
		}
		palabras.push_back(palabraDiccionario);
	}

	archivoDiccionario.close();

	sort(palabras.begin(), palabras.end(), sortDiccionario);

	return palabras;
}

/* Funcion que devuelve una palabra de la longitud del parametro con la frecuencia mas alta del diccionario segun su longitud */
vector<ocurrencia> Kasiski::busquedaPalabraEnDiccionarioPorLongitud(vector<char> palabraCriptograma) {

	vector<ocurrencia> palabrasADevovlerDiccionario;
	/* Busco desde los mas frecuentes a los menos frecuentes */
	vector<ocurrencia> palabrasDiccionario = getPalabrasDelDiccionario();
	sort(palabrasDiccionario.begin(), palabrasDiccionario.end(), sortDiccionarioPorFrecuencia);

	for (unsigned int i = 0; i < palabrasDiccionario.size(); i++) {
		/* Busco por la palabra de mayor frecuencia con la longitud de la palabra del criptograma */
		if (palabrasDiccionario.at(i).descripcion.size() == palabraCriptograma.size())
			palabrasADevovlerDiccionario.push_back(palabrasDiccionario.at(i));
	}
	return palabrasADevovlerDiccionario;
}

vector<ocurrencia> Kasiski::descifrarClave(vector<ocurrencia> palabraDiccionario, vector<char> palabraCriptograma) {

	vector<ocurrencia> palabraFinal;
	for (unsigned int j = 0; j < palabraDiccionario.size(); j++) {
		ocurrencia clave;
		for (unsigned int i = 0; i < palabraCriptograma.size(); i++) {
			clave.descripcion.push_back(palabraCriptograma.at(i) - palabraDiccionario.at(j).descripcion.at(i));
		}
		/* Hago rotacion de la clave */
		for (unsigned int j = 0; j < clave.descripcion.size(); j++) {
			char aux = clave.descripcion.front();
			for (unsigned int i = 0; i < clave.descripcion.size() - 1; i++) {
				clave.descripcion.at(i) = clave.descripcion.at(i + 1);
			}
			clave.descripcion.back() = aux;
			palabraFinal.push_back(clave);
		}
	}

	return palabraFinal;
}

vector<ocurrencia> Kasiski::ataqueKasiski() {

	char c;
	unsigned int i, j;
	unsigned int long_texto;
	string nombreArchivoCriptograma;
	char* textoEntrada = new char[1000];

	string path = this->pathEntrada;
	fstream archivoCriptograma;
	archivoCriptograma.open(path.c_str(), ios::in);

	/* No realizo operacion, vuelvo a pantalla anterior */
	if (archivoCriptograma.fail()) {
		cout << "\n\nERROR: no se puede abrir el archivo de entrada.\n\n";
		//		return 0;
	}

	/* Almacenado del contenido del archivo de entrada en una tabla */
	i = 0;
	while (archivoCriptograma.get(c)) {

		textoEntrada[i] = c;
		i = i + 1;
	}
	long_texto = i;
	archivoCriptograma.close();

	/* Obtengo la palabra mas grande y mas chica para saber el numero de pasadas a realizar */
	vector<ocurrencia> palabrasDiccionario = getPalabrasDelDiccionario();
	int longPalabraMasChica = palabrasDiccionario.front().descripcion.size();
	int longPalabraMasGrande = palabrasDiccionario.back().descripcion.size();

	for (unsigned int pasada = longPalabraMasChica; pasada <= longPalabraMasGrande; pasada++) {

		int posI = 0;

		while (posI <= (long_texto - pasada)) {

			vector<char> palabraLeida;

			for (unsigned int r = posI; r < pasada + posI; r++) {

				palabraLeida.push_back(textoEntrada[r]);
			}
			if (this->ocurrenciasEncontradas->size() == 0) {
				/* Agrego palabra a la lista de ocurrencias encontradas */
				ocurrencia palabraEncontrada;
				palabraEncontrada.frecuencia = 1;
				palabraEncontrada.descripcion = palabraLeida;
				this->ocurrenciasEncontradas ->push_back(palabraEncontrada);

			} else {
				/* Busco si la palabra ya estaba encontrada y aumento numero de apariciones */
				bool fueEncontrada = false;
				for (unsigned int i = 0; i < this->ocurrenciasEncontradas->size(); i++) {
					/*Veo si la palabra es igual*/
					if (palabraLeida.size() == this->ocurrenciasEncontradas->at(i).descripcion.size()) {

						bool esIgual = true;
						for (unsigned int p = 0; p < palabraLeida.size(); p++) {
							if (palabraLeida.at(p) != this->ocurrenciasEncontradas->at(i).descripcion.at(p)) {
								esIgual = false;
								break;
							}
						}

						if (esIgual) {
							this->ocurrenciasEncontradas->at(i).frecuencia++;
							fueEncontrada = true;
							break;
						}
					}

				}
				if (!fueEncontrada) {
					/* Agrego palabra a la lista de ocurrencias encontradas */
					ocurrencia palabraEncontrada;
					palabraEncontrada.frecuencia = 1;
					palabraEncontrada.descripcion = palabraLeida;
					this->ocurrenciasEncontradas ->push_back(palabraEncontrada);
				}
			}
			posI++;
		}
	}

	/* Ordeno vector de palabras encontradas por frecuencia y por longitud de las ocurrencias */
	sort(this->ocurrenciasEncontradas->begin(), this->ocurrenciasEncontradas->end(), ordenamientoPorFrecuenciaYLongitud());

	/* Palabra mas frecuente en criptograma */
	//	vector<char> palabraCriptograma = this->ocurrenciasEncontradas->back().descripcion;
	vector<ocurrencia> palabrasMasFrecuentes;
	int maximaFrecuencia = this->ocurrenciasEncontradas->front().frecuencia;
	for (unsigned int i = 0; i < this->ocurrenciasEncontradas->size(); i++) {
		if (this->ocurrenciasEncontradas->at(i).frecuencia == maximaFrecuencia) {
			palabrasMasFrecuentes.push_back(this->ocurrenciasEncontradas->at(i));
		} else {
			break;
		}
	}

//	/* Veo palabras con mayor frecuencia en el criptograma */
//	cout << "##########Empiezan las mas frecuentes" << endl;
//	for (unsigned int i = 0; i < palabrasMasFrecuentes.size(); i++) {
//		cout << "Key: ";
//		for (unsigned int j = 0; j < palabrasMasFrecuentes.at(i).descripcion.size(); j++) {
//			cout << palabrasMasFrecuentes.at(i).descripcion.at(j);
//		}
//		cout << " Cant.: ";
//		cout << palabrasMasFrecuentes.at(i).frecuencia << endl;
//	}
//	cout << "##########Treminan las mas frecuentes" << endl;
//
//	/* Veo todas las palabras del criptograma */
//
//	cout << "·######### empiezan todas las ocurrencias" << endl;
//	for (unsigned int i = 0; i < ocurrenciasEncontradas->size(); i++) {
//		/* Veo string con mayor frecuencia en el criptograma */
//		cout << "Key: ";
//		for (unsigned int j = 0; j < ocurrenciasEncontradas->at(i).descripcion.size(); j++) {
//			cout << ocurrenciasEncontradas->at(i).descripcion.at(j);
//		}
//		cout << " Cant.: ";
//		cout << ocurrenciasEncontradas->at(i).frecuencia << endl;
//	}
//	cout << "·######### terminan todas las ocurrencias" << endl;

	/* Busqueda palabra del diccionario de la longitud de la palabra mas frecuente del criptograma */
	vector<ocurrencia> clavesADevolver;
	for (unsigned int i = 0; i < palabrasMasFrecuentes.size(); i++) {
		vector<char> palabraCriptograma = palabrasMasFrecuentes.at(i).descripcion;
		vector<ocurrencia> claveDiccionario = busquedaPalabraEnDiccionarioPorLongitud(palabraCriptograma);

		/* Obtengo la clave del criptograma */

		vector<ocurrencia> clavesPosibles = descifrarClave(claveDiccionario, palabraCriptograma);

		for (unsigned int i = 0; i < clavesPosibles.size(); i++) {
			clavesADevolver.push_back(clavesPosibles.at(i));
		}

//		/* Imprimo claves posibles */
//		cout << "·######### empiezan las claves posibles" << endl;
//		for (unsigned int i = 0; i < clavesPosibles.size(); i++) {
//			for (unsigned int j = 0; j < clavesPosibles.at(i).descripcion.size(); j++) {
//				cout << clavesPosibles.at(i).descripcion.at(j);
//			}
//			cout << endl;
//		}
//		cout << "·######### terminan las claves posibles" << endl;
	}

	//	/* Obtengo la clave del criptograma */
	//	vector<ocurrencia> clavesPosibles = descifrarClave(claveDiccionario, palabraCriptograma);

	//	for (unsigned int i = 0; i < clavesPosibles.size(); i++) {
	//		for (unsigned int j = 0; j < clavesPosibles.at(i).descripcion.size(); j++) {
	//			cout << clavesPosibles.at(i).descripcion.at(j);
	//		}
	//		cout << "\n";
	//	}

	return clavesADevolver;
}

Kasiski::~Kasiski() {
	delete this->ocurrenciasEncontradas;
}
